package wafenginecore

import (
	"SamWaf/common/zlog"
	"SamWaf/global"
	"SamWaf/innerbean"
	"SamWaf/model"
	"SamWaf/model/wafenginmodel"
	"go.uber.org/zap"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strings"
	"time"
)

// 敏感文件和路径黑名单
var (
	// 路径穿越攻击模式
	pathTraversalPatterns = []*regexp.Regexp{
		regexp.MustCompile(`\.\.[\\/]`),
		regexp.MustCompile(`[\\/]\.\.[\\/]`),
		regexp.MustCompile(`%2e%2e[\\/]`),
		regexp.MustCompile(`%2e%2e%2f`),
		regexp.MustCompile(`%2e%2e%5c`),
		regexp.MustCompile(`\.\.[\\/%]`),
		regexp.MustCompile(`%252e%252e`),
		regexp.MustCompile(`%c0%ae%c0%ae`),
		regexp.MustCompile(`%uff0e%uff0e`),
		regexp.MustCompile(`[\\/]{2,}`),
		regexp.MustCompile(`\\{2,}`),
	}
)

// splitConfigString 分割配置字符串为数组，去除空白项
func splitConfigString(configStr string) []string {
	if configStr == "" {
		return []string{}
	}

	parts := strings.Split(configStr, ",")
	result := make([]string, 0, len(parts))

	for _, part := range parts {
		part = strings.TrimSpace(part)
		if part != "" {
			result = append(result, part)
		}
	}

	return result
}

// getDefaultSensitivePathsString 获取默认敏感路径配置字符串
func getDefaultSensitivePathsString() string {
	return "/etc/passwd,/etc/shadow,/etc/group,/etc/gshadow,/etc/hosts,/etc/hostname,/etc/resolv.conf,/etc/ssh/,/var/log/,/.ssh/,/.bash_history,/.profile,/.bashrc,/etc/crontab,/var/spool/cron/,/etc/apache2/,/etc/nginx/,/etc/httpd/,/var/www/,/usr/share/,/var/tmp/,/var/run/,c:\\windows\\,c:\\program files\\,c:\\program files (x86)\\,c:\\users\\,c:\\documents and settings\\,c:\\windows\\system32\\,c:\\windows\\syswow64\\,c:\\boot.ini,c:\\autoexec.bat,c:\\config.sys,\\windows\\,\\program files\\,\\program files (x86)\\,\\users\\,\\documents and settings\\,\\windows\\system32\\,\\windows\\syswow64\\,boot.ini,autoexec.bat,config.sys,ntuser.dat,pagefile.sys,hiberfil.sys,swapfile.sys"
}

// getDefaultSensitiveExtensionsString 获取默认敏感文件扩展名字符串
func getDefaultSensitiveExtensionsString() string {
	return ".key,.pem,.crt,.p12,.pfx,.jks,.bak,.backup,.old,.orig,.save,.sql,.db,.sqlite,.mdb,.env,.htaccess,.htpasswd,.git,.svn,.hg,.bzr,.DS_Store,Thumbs.db,desktop.ini,.tmp,.temp,.lock,.pid"
}

// getDefaultAllowedExtensionsString 获取默认允许的文件扩展名字符串
func getDefaultAllowedExtensionsString() string {
	return ".html,.htm,.css,.js,.json,.png,.jpg,.jpeg,.gif,.svg,.ico,.webp,.pdf,.txt,.md,.xml,.woff,.woff2,.ttf,.eot,.mp4,.webm,.ogg,.mp3,.wav,.zip,.tar,.gz,.rar"
}

// getDefaultSensitivePatternsString 获取默认敏感文件名模式字符串
func getDefaultSensitivePatternsString() string {
	return `(?i)\.git(/|\\),(?i)\.svn(/|\\),(?i)\.env,(?i)database\.(php|xml|json|yaml|yml),(?i)(backup|dump|export)\.(sql|db|tar|zip|gz),(?i)(id_rsa|id_dsa|id_ecdsa|id_ed25519),(?i)\.ssh(/|\\).*,(?i)(access|error|debug)\.log,(?i)web\.config,(?i)phpinfo\.php`
}

// getSensitivePathsFromConfig 从配置获取敏感路径数组
func getSensitivePathsFromConfig(config model.StaticSiteConfig) []string {
	if config.SensitivePaths != "" {
		return splitConfigString(config.SensitivePaths)
	}
	return splitConfigString(getDefaultSensitivePathsString())
}

// getSensitiveExtensionsFromConfig 从配置获取敏感扩展名数组
func getSensitiveExtensionsFromConfig(config model.StaticSiteConfig) []string {
	if config.SensitiveExtensions != "" {
		return splitConfigString(config.SensitiveExtensions)
	}
	return splitConfigString(getDefaultSensitiveExtensionsString())
}

// getAllowedExtensionsFromConfig 从配置获取允许扩展名数组
func getAllowedExtensionsFromConfig(config model.StaticSiteConfig) []string {
	if config.AllowedExtensions != "" {
		return splitConfigString(config.AllowedExtensions)
	}
	return splitConfigString(getDefaultAllowedExtensionsString())
}

// getSensitivePatternsFromConfig 从配置获取敏感模式数组
func getSensitivePatternsFromConfig(config model.StaticSiteConfig) []string {
	if config.SensitivePatterns != "" {
		return splitConfigString(config.SensitivePatterns)
	}
	return splitConfigString(getDefaultSensitivePatternsString())
}

// serveStaticFile 提供静态文件服务
func (waf *WafEngine) serveStaticFile(w http.ResponseWriter, r *http.Request, config model.StaticSiteConfig, weblog *innerbean.WebLog, hostsafe *wafenginmodel.HostSafe) bool {
	// 记录访问尝试
	startTime := time.Now()
	defer func() {
		zlog.Debug("静态文件访问完成",
			zap.String("path", r.URL.Path),
			zap.String("remote_addr", r.RemoteAddr),
			zap.Duration("duration", time.Since(startTime)))
	}()

	// 只允许GET和HEAD方法
	if r.Method != "GET" && r.Method != "HEAD" {
		waf.logSecurityEvent("非法HTTP方法", r.URL.Path, r.RemoteAddr, "method: "+r.Method, weblog)
		w.WriteHeader(http.StatusMethodNotAllowed)
		return true
	}

	// 检查URL长度限制
	if len(r.URL.Path) > 1024 {
		waf.logSecurityEvent("URL过长", r.URL.Path, r.RemoteAddr, "", weblog)
		w.WriteHeader(http.StatusRequestURITooLong)
		return true
	}

	// 提取相对路径（去掉前缀）
	relativePath := strings.TrimPrefix(r.URL.Path, config.StaticSitePrefix)
	if relativePath == "" {
		relativePath = "index.html" // 默认首页
	}

	// URL解码检查
	originalPath := relativePath
	if waf.containsEncodedThreats(relativePath) {
		waf.logSecurityEvent("检测到编码攻击", originalPath, r.RemoteAddr, "", weblog)
		w.WriteHeader(http.StatusForbidden)
		w.Write([]byte("403 Forbidden: Encoded threat detected"))
		return true
	}

	// 路径穿越攻击检查
	if waf.containsPathTraversal(relativePath) {
		waf.logSecurityEvent("路径穿越攻击", originalPath, r.RemoteAddr, "", weblog)
		w.WriteHeader(http.StatusForbidden)
		w.Write([]byte("403 Forbidden: Path traversal detected"))
		return true
	}

	// 敏感路径检查
	if waf.isSensitivePath(relativePath, config) {
		waf.logSecurityEvent("访问敏感路径", originalPath, r.RemoteAddr, "", weblog)
		w.WriteHeader(http.StatusForbidden)
		w.Write([]byte("403 Forbidden: Access to sensitive path denied"))
		return true
	}

	// 敏感文件检查
	if waf.isSensitiveFile(relativePath, config) {
		waf.logSecurityEvent("访问敏感文件", originalPath, r.RemoteAddr, "", weblog)
		w.WriteHeader(http.StatusForbidden)
		w.Write([]byte("403 Forbidden: Access to sensitive file denied"))
		return true
	}

	// 清理路径
	cleanPath := path.Clean(relativePath)

	// 二次路径穿越检查（清理后）
	if waf.containsPathTraversal(cleanPath) {
		waf.logSecurityEvent("清理后仍存在路径穿越", originalPath, r.RemoteAddr, "clean_path: "+cleanPath, weblog)
		w.WriteHeader(http.StatusForbidden)
		w.Write([]byte("403 Forbidden: Path traversal detected after cleaning"))
		return true
	}

	// 构建完整文件路径
	fullPath := filepath.Join(config.StaticSitePath, cleanPath)

	// 验证文件路径是否在允许的目录内
	absBasePath, err := filepath.Abs(config.StaticSitePath)
	if err != nil {
		zlog.Error("获取基础路径失败", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		return true
	}

	absFullPath, err := filepath.Abs(fullPath)
	if err != nil {
		zlog.Error("获取完整路径失败", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		return true
	}

	// 确保请求的文件在静态站点目录内
	if !strings.HasPrefix(absFullPath, absBasePath) {
		waf.logSecurityEvent("目录遍历攻击", originalPath, r.RemoteAddr,
			"full_path: "+fullPath+", abs_path: "+absFullPath, weblog)
		w.WriteHeader(http.StatusForbidden)
		w.Write([]byte("403 Forbidden: Directory traversal detected"))
		return true
	}

	// 检查文件是否存在和权限
	fileInfo, err := os.Stat(absFullPath)
	if err != nil {
		if os.IsNotExist(err) {
			waf.logSecurityEvent("文件未找到", originalPath, r.RemoteAddr, "", weblog)
			// 记录404但不详细记录路径信息，防止信息泄露
			zlog.Info("文件未找到", zap.String("remote_addr", r.RemoteAddr))
			w.WriteHeader(http.StatusNotFound)
			w.Write([]byte("404 Not Found"))
		} else if os.IsPermission(err) {
			waf.logSecurityEvent("文件权限拒绝", originalPath, r.RemoteAddr, "", weblog)
			w.WriteHeader(http.StatusForbidden)
			w.Write([]byte("403 Forbidden: Permission denied"))
		} else {
			zlog.Error("文件状态检查失败", zap.Error(err))
			w.WriteHeader(http.StatusInternalServerError)
			w.Write([]byte("500 Internal Server Error"))
		}
		return true
	}

	// 不允许访问目录
	if fileInfo.IsDir() {
		waf.logSecurityEvent("尝试访问目录", originalPath, r.RemoteAddr, "", weblog)
		w.WriteHeader(http.StatusForbidden)
		w.Write([]byte("403 Forbidden: Directory access denied"))
		return true
	}

	// 检查文件类型（基于扩展名）
	if !waf.isAllowedFileType(absFullPath, config) {
		waf.logSecurityEvent("不允许的文件类型", originalPath, r.RemoteAddr, "", weblog)
		w.WriteHeader(http.StatusForbidden)
		w.Write([]byte("403 Forbidden: File type not allowed"))
		return true
	}

	// 设置安全头
	waf.setSecurityHeaders(w)

	// 记录合法的静态文件访问到日志队列
	waf.logStaticFileAccess(r.URL.Path, r.RemoteAddr, fileInfo.Size(), weblog, hostsafe)

	// 提供文件服务
	http.ServeFile(w, r, absFullPath)
	return true
}

// containsPathTraversal 检查路径是否包含路径穿越攻击模式（增强版）
func (waf *WafEngine) containsPathTraversal(filePath string) bool {
	// 转换为小写进行检查
	lowerPath := strings.ToLower(filePath)

	// 使用正则表达式检查
	for _, pattern := range pathTraversalPatterns {
		if pattern.MatchString(lowerPath) {
			return true
		}
	}

	// 额外的字符串检查
	suspiciousPatterns := []string{
		"../", "..\\", ".../", "...\\",
		"%2e%2e%2f", "%2e%2e/", "..%2f",
		"%2e%2e%5c", "..%5c", "\\\\", "//",
		"%252e", "%c0%ae", "%uff0e",
	}

	for _, pattern := range suspiciousPatterns {
		if strings.Contains(lowerPath, pattern) {
			return true
		}
	}

	return false
}

// containsEncodedThreats 检查编码攻击
func (waf *WafEngine) containsEncodedThreats(filePath string) bool {
	lowerPath := strings.ToLower(filePath)

	// 检查各种编码绕过
	encodedThreats := []string{
		"%252e", "%c0%ae", "%uff0e", // 双重编码
		"%2e%2e%2f", "%2e%2e%5c", // URL编码
		"..%c0%af", "..%ef%bc%8f", // UTF-8编码
		"..%c1%9c", "..%c1%pc", // 畸形UTF-8
	}

	for _, threat := range encodedThreats {
		if strings.Contains(lowerPath, threat) {
			return true
		}
	}

	return false
}

// isSensitivePath 检查是否为敏感路径
func (waf *WafEngine) isSensitivePath(filePath string, config model.StaticSiteConfig) bool {
	lowerPath := strings.ToLower(filePath)

	// 从配置获取敏感路径数组
	sensitivePaths := getSensitivePathsFromConfig(config)

	// 检查敏感路径
	for _, sensitivePath := range sensitivePaths {
		if strings.Contains(lowerPath, strings.ToLower(sensitivePath)) {
			return true
		}
	}

	return false
}

// isSensitiveFile 检查是否为敏感文件
func (waf *WafEngine) isSensitiveFile(filePath string, config model.StaticSiteConfig) bool {
	lowerPath := strings.ToLower(filePath)
	fileName := filepath.Base(lowerPath)

	// 从配置获取敏感扩展名数组
	sensitiveExtensions := getSensitiveExtensionsFromConfig(config)

	// 检查敏感文件扩展名
	for _, ext := range sensitiveExtensions {
		if strings.HasSuffix(fileName, ext) {
			return true
		}
	}

	// 从配置获取敏感模式数组
	sensitivePatterns := getSensitivePatternsFromConfig(config)

	// 检查敏感文件名模式
	for _, patternStr := range sensitivePatterns {
		if pattern, err := regexp.Compile(patternStr); err == nil {
			if pattern.MatchString(lowerPath) {
				return true
			}
		}
	}

	// 检查隐藏文件（以.开头，但不是../或./）
	if strings.HasPrefix(fileName, ".") && fileName != "." && fileName != ".." {
		return true
	}

	return false
}

// isAllowedFileType 检查是否为允许的文件类型
func (waf *WafEngine) isAllowedFileType(filePath string, config model.StaticSiteConfig) bool {
	// 从配置获取允许扩展名数组
	allowedExtensions := getAllowedExtensionsFromConfig(config)

	ext := strings.ToLower(filepath.Ext(filePath))
	if ext == "" {
		return false // 没有扩展名的文件不允许
	}

	for _, allowedExt := range allowedExtensions {
		if ext == allowedExt {
			return true
		}
	}

	return false
}

// setSecurityHeaders 设置安全响应头
func (waf *WafEngine) setSecurityHeaders(w http.ResponseWriter) {
	w.Header().Set("X-Content-Type-Options", "nosniff")
	w.Header().Set("X-Frame-Options", "DENY")
	w.Header().Set("X-XSS-Protection", "1; mode=block")
	w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
	w.Header().Set("Content-Security-Policy", "default-src 'self'")
	w.Header().Set("Cache-Control", "public, max-age=3600")
}

// logStaticFileAccess 记录成功的静态文件访问到传入的weblog
func (waf *WafEngine) logStaticFileAccess(path, remoteAddr string, fileSize int64, weblog *innerbean.WebLog, hostsafe *wafenginmodel.HostSafe) {
	// 更新weblog信息
	weblog.ACTION = "放行"
	weblog.RULE = "静态文件访问成功"
	weblog.RISK_LEVEL = 0 // 无风险

	// 按照全局日志记录策略决定是否记录
	if global.GWAF_RUNTIME_RECORD_LOG_TYPE == "all" {
		if hostsafe.Host.EXCLUDE_URL_LOG == "" {
			global.GQEQUE_LOG_DB.Enqueue(weblog)
		} else {
			lines := strings.Split(hostsafe.Host.EXCLUDE_URL_LOG, "\n")
			isRecordLog := true
			// 检查每一行
			for _, line := range lines {
				if strings.HasPrefix(weblog.URL, line) {
					isRecordLog = false
				}
			}
			if isRecordLog {
				global.GQEQUE_LOG_DB.Enqueue(weblog)
			}
		}
	} else if global.GWAF_RUNTIME_RECORD_LOG_TYPE == "abnormal" && weblog.ACTION != "放行" {
		// 对于静态文件成功访问，ACTION是"放行"，所以在abnormal模式下不会记录
		// 这里保持逻辑一致性，虽然实际上不会执行
		global.GQEQUE_LOG_DB.Enqueue(weblog)
	}
}

// logSecurityEvent 记录安全事件
func (waf *WafEngine) logSecurityEvent(eventType, path, remoteAddr, details string, weblog *innerbean.WebLog) {
	// 更新weblog信息
	weblog.ACTION = "禁止"
	weblog.RULE = "静态文件安全检查: " + eventType
	weblog.RISK_LEVEL = 3 // 高风险

	// 入队到日志系统
	global.GQEQUE_LOG_DB.Enqueue(weblog)

	// 同时记录到系统日志用于调试
	zlog.Warn("静态文件安全事件",
		zap.String("event_type", eventType),
		zap.String("path", path),
		zap.String("remote_addr", remoteAddr),
		zap.String("details", details))
}

// 初始化静态站点配置的默认值
func InitDefaultStaticSiteConfig() model.StaticSiteConfig {
	return model.StaticSiteConfig{
		IsEnableStaticSite:  0,
		StaticSitePath:      "",
		StaticSitePrefix:    "/",
		SensitivePaths:      getDefaultSensitivePathsString(),
		SensitiveExtensions: getDefaultSensitiveExtensionsString(),
		AllowedExtensions:   getDefaultAllowedExtensionsString(),
		SensitivePatterns:   getDefaultSensitivePatternsString(),
	}
}
